#include "mongo_system.h"

// 递归获取消息结构
std::string MongoSystem::getMessageStructure(const Message* message, int indent_level) {
    std::string result;
    std::string indent(indent_level * 2, ' ');

    const Descriptor* descriptor = message->GetDescriptor();
    const Reflection* reflection = message->GetReflection();

    for (int i = 0; i < descriptor->field_count(); ++i) {
        const FieldDescriptor* field = descriptor->field(i);

        result += indent + "├─ " + field->name() + ": ";

        // 字段类型
        std::string type_str = getFieldTypeString(field);
        result += type_str;

        // 是否重复
        if (field->is_repeated()) {
            result += " (array)";
        }

        // 是否必需
        if (field->is_required()) {
            result += " [required]";
        }

        result += "\n";

        // 如果是嵌套消息，递归处理
        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_repeated()) {
            const Message& sub_message = reflection->GetMessage(*message, field);
            result += getMessageStructure(&sub_message, indent_level + 1);
        }
    }

    return result;
}

// 获取字段类型字符串
std::string MongoSystem::getFieldTypeString(const FieldDescriptor* field) {
    switch (field->type()) {
        case FieldDescriptor::TYPE_DOUBLE:
            return "double";
        case FieldDescriptor::TYPE_FLOAT:
            return "float";
        case FieldDescriptor::TYPE_INT64:
            return "int64";
        case FieldDescriptor::TYPE_UINT64:
            return "uint64";
        case FieldDescriptor::TYPE_INT32:
            return "int32";
        case FieldDescriptor::TYPE_FIXED64:
            return "fixed64";
        case FieldDescriptor::TYPE_FIXED32:
            return "fixed32";
        case FieldDescriptor::TYPE_BOOL:
            return "bool";
        case FieldDescriptor::TYPE_STRING:
            return "string";
        case FieldDescriptor::TYPE_MESSAGE:
            return "object (" + field->message_type()->name() + ")";
        case FieldDescriptor::TYPE_BYTES:
            return "bytes";
        case FieldDescriptor::TYPE_UINT32:
            return "uint32";
        case FieldDescriptor::TYPE_ENUM:
            return "enum (" + field->enum_type()->name() + ")";
        case FieldDescriptor::TYPE_SFIXED32:
            return "sfixed32";
        case FieldDescriptor::TYPE_SFIXED64:
            return "sfixed64";
        case FieldDescriptor::TYPE_SINT32:
            return "sint32";
        case FieldDescriptor::TYPE_SINT64:
            return "sint64";
        default:
            return "unknown";
    }
}

// ============================================================================
// MongoSystem Implementation
// ============================================================================

MongoSystem::MongoSystem() : isInit(false) {}

MongoSystem::~MongoSystem() {}

int32_t MongoSystem::Init(const std::string& url) {
    try {
        client = mongocxx::client{mongocxx::uri{url}};
        isInit = true;
        std::cout << "MongoSystem initialized successfully with URL: " << url << std::endl;
        return 0;
    } catch (const std::exception& e) {
        std::cerr << "Failed to initialize MongoSystem: " << e.what() << std::endl;
        isInit = false;
        return -1;
    }
}

int32_t MongoSystem::InsertOne(const std::string& collectionName, bsoncxx::v_noabi::document::view_or_value document,
                               mongocxx::options::insert const& options) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        // Parse collection name: "database.collection"
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format. Expected: "
                         "'database.collection'"
                      << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        auto result = coll.insert_one(document.view());

        if (result) {
            std::cout << "Inserted document with _id: " << result->inserted_id().get_oid().value.to_string()
                      << std::endl;
            return 0;
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in InsertOne: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::InsertMany(const std::string& collectionName,
                                const std::vector<bsoncxx::document::value>& documents) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    if (documents.empty()) {
        std::cerr << "No documents to insert" << std::endl;
        return -1;
    }

    try {
        // Parse collection name
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Insert multiple documents
        auto result = coll.insert_many(documents);

        if (result) {
            std::cout << "Inserted " << result->inserted_count() << " documents" << std::endl;
            return result->inserted_count();
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in InsertMany: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::FindOne(const std::string& collectionName, const bsoncxx::document::view_or_value& filter,
                             bsoncxx::document::value* result) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Find one document with filter
        auto find_result = coll.find_one(filter.view());

        if (find_result) {
            std::cout << "Found document: " << bsoncxx::to_json(find_result->view()) << std::endl;
            if (result) {
                *result = bsoncxx::document::value(find_result->view());
            }
            return 0;
        } else {
            std::cout << "No document found" << std::endl;
            return -1;
        }

    } catch (const std::exception& e) {
        std::cerr << "Error in FindOne: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::Find(const std::string& collectionName, const bsoncxx::document::view_or_value& filter,
                          const mongocxx::options::find& options, std::vector<bsoncxx::document::value>* results) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Find documents with filter and options
        auto cursor = coll.find(filter.view(), options);

        int count = 0;
        for (auto&& doc : cursor) {
            std::cout << "Document " << ++count << ": " << bsoncxx::to_json(doc) << std::endl;
            if (results) {
                results->push_back(bsoncxx::document::value(doc));
            }
        }

        std::cout << "Total documents found: " << count << std::endl;
        return count;

    } catch (const std::exception& e) {
        std::cerr << "Error in Find: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::UpdateOne(const std::string& collectionName, const bsoncxx::document::view_or_value& filter,
                               const bsoncxx::document::view_or_value& update) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Update one document
        auto result = coll.update_one(filter.view(), update.view());

        if (result) {
            std::cout << "Matched " << result->matched_count() << " document(s), modified " << result->modified_count()
                      << " document(s)" << std::endl;
            return result->modified_count();
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in UpdateOne: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::UpdateMany(const std::string& collectionName, const bsoncxx::document::view_or_value& filter,
                                const bsoncxx::document::view_or_value& update) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Update many documents
        auto result = coll.update_many(filter.view(), update.view());

        if (result) {
            std::cout << "Matched " << result->matched_count() << " document(s), modified " << result->modified_count()
                      << " document(s)" << std::endl;
            return result->modified_count();
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in UpdateMany: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::DeleteMany(const std::string& collectionName, const bsoncxx::document::view_or_value& filter) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Delete many documents
        auto result = coll.delete_many(filter.view());

        if (result) {
            std::cout << "Deleted " << result->deleted_count() << " document(s)" << std::endl;
            return result->deleted_count();
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in DeleteMany: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::DeleteOne(const std::string& collectionName, const bsoncxx::document::view_or_value& filter) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Delete one document
        auto result = coll.delete_one(filter.view());

        if (result) {
            std::cout << "Deleted " << result->deleted_count() << " document(s)" << std::endl;
            return result->deleted_count();
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in DeleteOne: " << e.what() << std::endl;
        return -1;
    }
}

int32_t MongoSystem::ReplaceOne(const std::string& collectionName, const bsoncxx::document::view_or_value& filter,
                                const bsoncxx::document::view_or_value& replacement) {
    if (!isInit) {
        std::cerr << "MongoSystem not initialized" << std::endl;
        return -1;
    }

    try {
        size_t pos = collectionName.find('.');
        if (pos == std::string::npos) {
            std::cerr << "Invalid collection name format" << std::endl;
            return -1;
        }

        std::string db_name = collectionName.substr(0, pos);
        std::string coll_name = collectionName.substr(pos + 1);

        auto db = client[db_name];
        auto coll = db[coll_name];

        // Replace one document
        auto result = coll.replace_one(filter.view(), replacement.view());

        if (result) {
            std::cout << "Matched " << result->matched_count() << " document(s), modified " << result->modified_count()
                      << " document(s)" << std::endl;
            return result->modified_count();
        }

        return -1;

    } catch (const std::exception& e) {
        std::cerr << "Error in ReplaceOne: " << e.what() << std::endl;
        return -1;
    }
}
